喜歡跳著寫的後果就是有時候會本末倒置,像今天終於看了前幾天逃避的 Memorization(記憶化搜索),結果發現應該擺在 DP 第一天的,不過就當個複習吧。
講到 memorization 經常會提到 recursion 和 iteration。iteration 就是進行重複的操作,像是做 DP 時依照規律把表格填滿那樣;recursion 則是重複呼叫某個東西,把大問題切小。用 Fibonacci 數列為例,recursion 是用 F(n) = F(n-1) + F(n-2) = [F(n-2)+F(n-3)] + [F(n-3)+F(n-4)] ...
這樣一副切下去,iteration 則是從 F(1) 開始往上一路算到 F(n)。
Memorizaion 基本上大概是 recursion 加上記憶功能,透過記錄遍歷過的狀態訊息,避免一再執行重複動作以降低複雜度。不過相對於 DP 的方法,因為一個狀態有可能被訪問多次(DP 一個狀態就是被計算一次),因此效率會比較低。
403. Frog Jump 這題寫起來挺直觀的,就是不停的紀錄當下的狀態,然後在動作的同時和歷史狀態做對照,確認最終有沒有辦法達到目標狀態。
class Solution:
def canCross(self, stones: List[int]) -> bool:
if stones[1] != 1:
return False
s = set(stones)
visit = defaultdict(set)
q = [[1,1]]
final = stones[-1]
nxt = [-1,0,1]
while q:
cur, step = q.pop(0)
for i in nxt:
tmp = step+i
if tmp < 0:
continue
tmp2 = cur+tmp
if tmp2 in visit and tmp in visit[tmp2]:
continue
elif tmp2 == final:
return True
elif tmp2 > final:
continue
if tmp2 in s:
q.append([tmp2, tmp])
visit[tmp2].add(tmp)
return False
附上 DP 版的解法,開 n^2 矩陣的話會爆時間,所以就用 hash table 來記了。
class Solution:
def canCross(self, stones: List[int]) -> bool:
n = len(stones)
dp = {stone: set() for stone in stones}
dp[0].add(0)
for i in range(n):
for k in dp[stones[i]]:
for step in [k - 1, k, k + 1]:
if step > 0 and stones[i] + step in dp:
dp[stones[i] + step].add(step)
return bool(dp[stones[-1]])